package com.bubblegumproject.ogo.component;

import com.bubblegumproject.ogo.OgoException;
import com.bubblegumproject.ogo.action.Action;
import com.bubblegumproject.ogo.action.ApplicationException;
import org.apache.log4j.Logger;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * ComponentInvocation to be used when invoking ActionHandler methods.
 *
 * @author Azubuko Obele (buko.obele@gmail.com)
 */
public class ActionHandlerComponentInvocation extends ComponentInvocation {

    private Class eventClazz;

    private ActionHandler metadata;

    private static final Logger log = Logger.getLogger(ActionHandlerComponentInvocation.class);

    public ActionHandlerComponentInvocation(Method method) {
        super(method);

        if (!method.isAnnotationPresent(ActionHandler.class))
            throw new IllegalArgumentException(method + " had no annotation " + ActionHandler.class);

        checkMethodIsValidEventHandler(method);
        this.metadata = method.getAnnotation(ActionHandler.class);
        this.eventClazz = method.getParameterTypes()[0];
    }

    public Class getEventClass() {
        return eventClazz;
    }

    public ActionHandler getMetadata() {
        return metadata;
    }

    /**
     * ActionHandlerComponentInvocations will intercept any exception class annotated with
     * {@link @com.bubblegumproject.ogo.action.ApplicationException}. This error will be added to the Action's
     * error stack and <strong>the exception will not be rethrown</strong>. This provides for automatic handling of
     * an application's error protocol.
     *
     * SMELL: There's a bit of a problem with the logic here. In this case we are adding any thrown exceptions to the
     * request action. NOT to the response Action. This only works if the request Action is the same as the
     * response Action.  
     */
    public Object invoke(Object target, Object... arguments) {
        Action currentAction = (Action) arguments[0];
        try {
            return getMethod().invoke(target, arguments);
        } catch (InvocationTargetException ite) {
            if (ite.getCause().getClass().isAnnotationPresent(ApplicationException.class)) {
                log.debug(this + " encountered ApplicationException " + ite.getCause() +
                        " for currentAction " + currentAction + " ! Will add it to action error stack and swallow it!");
                currentAction.errors().push(ite.getCause());
                return currentAction; 
            } else {
                throw new ComponentInvocationException(ite.getCause());
            }
        } catch (IllegalAccessException iae) {
            throw new OgoException(this + " encountered unexpected exception", iae);
        }
    }

    private void checkMethodIsValidEventHandler(Method method) {
        // a method is a valid ActionHandler if it takes a single parameter whose type is an Action
        Class[] params = method.getParameterTypes();
        if (params.length != 1) throw new IllegalArgumentException(method + "must take single argument of type Action");
        if (!Action.class.isAssignableFrom(params[0]))
            throw new IllegalArgumentException(method + " parameter must be of type " + Action.class);
    }

}
